import os
import sys
import numpy as np
import datetime
import struct

from PySide2.QtWidgets import QApplication, QMainWindow, QMessageBox
from PySide2.QtUiTools import QUiLoader
from PySide2.QtCore import QFile, Signal, QThread, QTimer

import pyqtgraph as pg

import serial
import serial.tools.list_ports

from device import DeviceRecieveWorker




BASEDIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(BASEDIR)
if sys.argv[0].split('.')[1] == 'exe':
    platforms_path = os.path.join(BASEDIR, 'lib', 'PySide2', 'plugins', 'platforms')
    os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = platforms_path


class SerialerWindow(QMainWindow):
    start_background_work = Signal()
    def __init__(self):
        super().__init__()
        loader = QUiLoader()
        file = QFile(os.path.join(BASEDIR, 'app.ui'))
        file.open(QFile.ReadOnly)
        file.close()
        self.ui = loader.load(file)
        self.ui.setWindowTitle("串口调试")
        self.ui.show()#Maximized()
        self.setup()

    def setup(self):
        '''
        设置ui
        '''
        ports = [each[0] for each in serial.tools.list_ports.comports()]
        for each in ports:
            self.ui.port.addItem(each)
        for k in range(1, 10):
            if f"COM{k}" not in ports:
                self.ui.port.addItem(f"COM{k}")
        self.device_thread = QThread()
        self.logs = []
        self.pw = pg.PlotWidget()
        colors = ['#DC143C', '#FFA500', '#32CD32', '#00CED1', '#D2691E']
        self.plots = [np.empty([1,]) for _ in range(5)]
        for k in range(5):
            self.plots[k] = np.delete(self.plots[k], 0, 0)
        self.curves = [pg.PlotCurveItem(pen=colors[k]) for k in range(5)]
        for curve in self.curves:
            self.pw.addItem(curve)
        for k in range(5):
            self.curves[k].setData(self.plots[k])
        self.ui.plotLayout.addWidget(self.pw, 0)
        self.buffers = []
        self.timer = QTimer()
        self.parseTimer = QTimer()
        self.signal()
        self.parseIdx = 0

    def signal(self):
        '''
        绑定信号
        '''
        self.timer.timeout.connect(self.showBuffer)
        self.timer.start(100)
        self.parseTimer.timeout.connect(self.parseBuffer)
        self.parseTimer.start(2)
        self.ui.connectdevice.clicked.connect(self.connectdevice)
        self.ui.cleardata.clicked.connect(self.clearScreen)
        self.ui.senddata.clicked.connect(self.senddata)
    
    def senddata(self):
        data = self.ui.text.text()
        if self.ui.sendhex.isChecked():
            data.replace(' ', '')
            data = bytearray.fromhex(data)
            self.device.write(data)
        else:
            self.device.write(data.encode('utf-8'))
        self.ui.senddata.setChecked(False)

    def connectdevice(self):
        if self.ui.connectdevice.text() == "关闭串口":
            self.device.close()
            self.ui.connectdevice.setText("打开串口")
            self.ui.senddata.setEnabled(False)
            if self.device_thread.isRunning():
                self.device_thread.quit()
                self.device_thread.wait()
            del self.device
            del self.device_worker
        else:
            bytesizes =  [serial.EIGHTBITS, serial.SEVENBITS, serial.SIXBITS, serial.FIVEBITS]
            paritys = [
                serial.PARITY_NONE, serial.PARITY_EVEN, serial.PARITY_ODD,
                serial.PARITY_MARK, serial.PARITY_SPACE]
            stopbits = [serial.STOPBITS_ONE, serial.STOPBITS_ONE_POINT_FIVE, serial.STOPBITS_TWO]
            
            try:
                self.device = serial.Serial(
                    self.ui.port.currentText(),
                    self.ui.baudrate.currentText(),
                    bytesize=bytesizes[self.ui.bytesize.currentIndex()],
                    parity=paritys[self.ui.parity.currentIndex()],
                    stopbits=stopbits[self.ui.stopbits.currentIndex()],
                    timeout=self.ui.timeout.value() / 1000
                )
            except:
                QMessageBox.warning(self, "警告", "串口打开失败")
            else:
                self.device_thread.start()
                self.ui.connectdevice.setText("关闭串口")
                self.ui.senddata.setEnabled(True)
                self.device_worker = DeviceRecieveWorker(
                    self.device, self.ui.framelength.value(), self.ui.framehead.text()
                )
                self.device_worker.moveToThread(self.device_thread)
                self.start_background_work.connect(self.device_worker.run)
                self.start_background_work.emit()
                self.device_worker.gotbuffer.connect(self.gotbuffer)

    def gotbuffer(self, buffer):
        now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
        self.buffers.insert(0, [now, buffer])
    
    def showBuffer(self):
        while self.buffers:
            now, buffer = self.buffers.pop()
            hexstr = ' '.join([format(buff, '02x').upper() for buff in buffer])
            self.ui.logs.insertItem(0,
                "%s %s %s" % (
                    now,
                    " ←← ",
                    hexstr
                )
            )
        for k in range(5):
            self.curves[k].setData(self.plots[k])
        
    def parseBuffer(self):
        cnt = self.ui.logs.count()
        if self.parseIdx >= cnt:
            return
        item = self.ui.logs.item(cnt - self.parseIdx - 1)
        self.parseIdx += 1
        if item is None:
            return
        buffer = bytes.fromhex(item.text()[32:].replace(" ", ""))
        head = bytes.fromhex(self.ui.framehead.text().replace(" ", ""))
        rear = bytes.fromhex(self.ui.framerear.text().replace(" ", ""))
        framedefine = self.ui.framedefine.text().lower()
        datas = struct.unpack(framedefine, buffer[len(head):-len(rear)])
        item.setText(item.text() + ' ' + ' '.join([f"{each:.3f}" for each in datas]))
        # curves
        for k in range(5):
            try:
                nums = eval(f"[int(each)-1 for each in self.ui.plotline_{k + 1}.text().split(',')]")
            except:
                continue
            self.plots[k] = np.append(self.plots[k], [datas[m] for m in nums])


    def clearScreen(self):
        self.ui.logs.clear()
        self.buffers = []
        self.parseIdx = 0
        for k in range(5):
            self.plots[k] = np.delete(self.plots[k], slice(0, -1), 0)
    


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setStyle("fusion")
    win = SerialerWindow()
    sys.exit(app.exec_())
